package spitfire.ksim.algorithm;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Algorithm to calculate the similarity score between a raw data and a {@link FuzzyRule}.
 * @author Adam
 *
 */
public class SimilarityAlgorithm {

	/* 
	 * No  treatment for reodering effect
	 */
//	public static double calculateScore(List<Double> dataList, FuzzyRule rule) {
//		if (rule.size() < 2) {
//			throw new RuntimeException("size too small");
//		}
//		
//		double rawMax = Collections.max(dataList);
//		double rawMin = Collections.min(dataList);
//		
//		ArrayList<Double> xList = rule.getxList();
//		ArrayList<Double> yList = rule.getyList();
//		double ruleRMax = rule.getrMax();
//		double ruleRMin = rule.getrMin();
//		
//		double drange = Math.abs(rawMin-ruleRMin) + Math.abs(rawMax-ruleRMax);
//		
//		double sc = 0;
//		for (int i = 0; i < dataList.size(); i++) {
//			double dataValue = dataList.get(i);
//			int p1 = Collections.binarySearch(xList, dataValue);
//			int p2 = 0;
//			if (p1 >= 0 ) {
//				// found
//				sc += yList.get(p1);
//			} else {
//				// not found
//				p1 = -p1 - 1;
//				if (p1 == 0) {
//					// smaller than min
//				} else if (p1 == rule.size()) {
//					// bigger than max
////					p1 = p1 - 2;
//				} else {
//					// data value between rule range
//					p1--;
//					p2 = p1 + 1;
//					double x1 = xList.get(p1);
//					double x2 = xList.get(p2);
//					double y1 = yList.get(p1);
//					double y2 = yList.get(p2);
//					sc += (x1*y2-y1*x2)/(x1-x2) + (y2-y1)/(x2-x1)*dataValue;
//				}
//			}
//		}
//		sc /= drange;
//		return sc;
//	}
	
	/* 
	 * Derivative to overcome reodering effect
	 */
	public static double calculateScore(List<Double> dataList, FuzzyRule rule) {
		if (rule.size() < 2) {
			throw new RuntimeException("size too small");
		}
		
		double rawMax = Collections.max(dataList);
		double rawMin = Collections.min(dataList);
		
		ArrayList<Double> xList = rule.getxList();
		ArrayList<Double> yList = rule.getyList();
		double ruleRMax = rule.getrMax();
		double ruleRMin = rule.getrMin();
		
		double drange = Math.abs(rawMin-ruleRMin) + Math.abs(rawMax-ruleRMax);
		
		double sc = 0;
		for (int i = 1; i < dataList.size(); i++) {
			double dataValue = dataList.get(i);
			
			int p1 = Collections.binarySearch(xList, dataValue);
			int p2 = 0;
			if (p1 >= 0 ) {
				// found
				sc += yList.get(p1);
			} else {
				// not found
				p1 = -p1 - 1;
				if (p1 == 0) {
					// smaller than min
				} else if (p1 == rule.size()) {
					// bigger than max
//					p1 = p1 - 2;
				} else {
					// data value between rule range
					p1--;
					p2 = p1 + 1;
					double x1 = xList.get(p1);
					double x2 = xList.get(p2);
					double y1 = yList.get(p1);
					double y2 = yList.get(p2);
					sc += (x1*y2-y1*x2)/(x1-x2) + (y2-y1)/(x2-x1)*dataValue;
				}
			}
		}
		sc /= drange;
		return sc;
	}
}
